package com.yunchang.open.iploaction;

import com.yunchang.open.iploaction.analyzer.DefaultLocationAnalyzer;
import com.yunchang.open.iploaction.analyzer.LocationAnalyzer;
import com.yunchang.open.iploaction.parser.IPv4Parser;
import com.yunchang.open.iploaction.parser.cz88.Cz88Parser;
import com.yunchang.open.iploaction.parser.ipip.IpipParser;
import com.yunchang.open.iploaction.utils.LocationUtil;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

/**
 * <pre>
 * IP归属地工具类。使用时注意以下几点：
 * 1.该类的方法都是线程安全的；
 * 2.可以通过{@link IPLocationFactory}便捷的创建IPLocationHelper实例，也可以自行封装成单例，或者通过Spring容器管理IPLocationHelper对象；
 * 3.支持使用一个（或多个）ip库进行地址解析，默认提供{@link IpipParser}和{@link Cz88Parser}，
 * 使用者如果需要添加自定义IP解析器{@link IPv4Parser}，调用{@link IPLocationHelper#addParser(Class, InputStream)}接口进行添加即可
 * 4.当同时使用多个ip库时，一个ip实际上可能会得出多个归属地，默认使用{@link DefaultLocationAnalyzer}选择可信度最高的归属地；
 * 使用者如果需要设置为自定义的地区分析器{@link LocationAnalyzer}，调用{@link IPLocationHelper#setAnalyzer(LocationAnalyzer)}接口进行添加即可
 * </pre>
 * Created by jasontujun on 2019/6/24.
 */
public class IPLocationHelper {

    private static final Pattern IPV4_PATTERN = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
    private Comparator<IPv4Parser> parserComparator = new Comparator<IPv4Parser>() {
        @Override
        public int compare(IPv4Parser o1, IPv4Parser o2) {
            return o2.getPriority() - o1.getPriority();
        }
    };
    private Comparator<ParseResult> analyseComparator = new Comparator<ParseResult>() {
        @Override
        public int compare(ParseResult r1, ParseResult r2) {
            return Integer.compare(r2.score, r1.score);
        }
    };

    private List<IPv4Parser> parsers;
    private Set<Class> parserClasses;
    private LocationAnalyzer analyzer;

    public IPLocationHelper() {
        parsers = new CopyOnWriteArrayList<IPv4Parser>();
        parserClasses = new HashSet<Class>();
        analyzer = new DefaultLocationAnalyzer();
    }

    /**
     * 添加解释器。
     * 通过ip库文件，初始化解析器。
     *
     * @param parserClass 解释器类型
     * @param inputStream ip库文件流
     * @throws Exception 解析器类不存在，或者解析器初始化失败，会抛出异常
     */
    public synchronized IPLocationHelper addParser(Class<? extends IPv4Parser> parserClass,
                                                   InputStream inputStream) throws Exception {
        if (parserClasses.contains(parserClass)) {
            // 已经添加过同类型解释器，无需再添加
            return this;
        }
        if (inputStream == null) {
            throw new IllegalArgumentException("InputStream could not be null");
        }
        IPv4Parser parser = parserClass.newInstance();
        parser.init(inputStream);
        parsers.add(parser);
        parserClasses.add(parserClass);
        //优先级越大，排序后，有多个解析器时就会优先调用优先级大的解析器
        Collections.sort(parsers, parserComparator);
        return this;
    }

    /**
     * 获取ip对应的地区
     *
     * @param ip ip地址
     * @return 返回地区；如果ip地址异常，可能返回null
     */
    public Location findLocation(String ip) {
        if (ip == null || ip.isEmpty()) {
            return null;
        }
        Location location = null;
        // 目前只能解析ipv4地址
        if (IPV4_PATTERN.matcher(ip).matches()) {
            LocationAnalyzer locationAnalyzer = analyzer;
            if (locationAnalyzer != null) {
                // 如果设定了地区分析器，则依次解析归属地
                List<ParseResult> results = new ArrayList<ParseResult>();
                for (IPv4Parser parser : parsers) {
                    location = parser.findIpv4Location(ip);
                    int score = locationAnalyzer.score(ip, parser, location);
                    ParseResult parseResult = new ParseResult(location, parser, score);
                    results.add(parseResult);
                    if (!locationAnalyzer.accept(ip, parser, location, score)) {
                        break;
                    }
                }
                // 选择分数最高的地区，作为结果返回
                if (!results.isEmpty()) {
                    if (results.isEmpty()) {
                        return null;
                    }
                    if (results.size() > 1) {
                        // 返回信息更完整的地址（如果都很完整，则返回优先级较高的解析器的结果）
                        Collections.sort(results, analyseComparator);
                    }
                    location = results.get(0).location;
                }
            } else {
                // 如果没设定地区分析器，依次解析归属地，第一个解析出非空归属地即返回
                for (IPv4Parser parser : parsers) {
                    location = parser.findIpv4Location(ip);
                    if (!LocationUtil.isUnknownLocation(location)) {
                        return location;
                    }
                }
            }
        }
        return location;
    }

    /**
     * 设置地区分析器，用于筛选多个地区结果。
     *
     * @param analyzer 地区分析器
     * @see LocationAnalyzer
     */
    public void setAnalyzer(LocationAnalyzer analyzer) {
        this.analyzer = analyzer;
    }

    /**
     * 是否包含至少一个ip解析器
     */
    public boolean hasParser() {
        return parsers.isEmpty();
    }

    private class ParseResult {
        public Location location;
        public IPv4Parser parser;
        public int score; //地址详细打分


        public ParseResult(Location location, IPv4Parser parser, int score) {
            this.location = location;
            this.parser = parser;
            this.score = score;
        }
    }
}
